﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Data.SqlTypes;
using System.Linq;

using Noris.Schedule.Support;
using Noris.Schedule.Support.Data;
using Noris.Schedule.Support.Sql;
using Noris.Schedule.Support.DataFace;
using Noris.Schedule.Support.Services;

using Noris.Schedule.Planning.DataFace;
using System.Drawing;
using Noris.Schedule.Planning.ProcessData;

namespace Noris.Schedule.Planning.ProcessData
{
	#region CLASS AxisDistilleMap : vydestilované informace z dat plánovacích tabulí (AxisS a TaskC) plus vztahy.
	/// <summary>
	/// AxisDistilleMap : vydestilované informace z dat plánovacích tabulí (AxisS a TaskC) plus vztahy.
	/// Vznikne destilací osy, destilátorem je statický konstruktor této třídy (AxisDistilleMap.DistillateAxisMapFrom()).
	/// Obsahuje nejjemnější esenci z plánu = informaci o záznamech osy S a tasků, a vztahy mezi těmito prvky.
	/// Neobsahuje kopie dat ze vstupních záznamů, ani reference na tyto záznamy. To je třeba dohledat v modulu AxisHeap.
	/// Slouží jako základna pro přeplánování dat.
	/// </summary>
	internal class AxisDistilleMap
	{
		#region KONSTRUKCE
		private AxisDistilleMap()
		{
			this._MapDict = new Dictionary<GID, AxisDistilleItemMap>();
			this._MapList = new List<AxisDistilleItemMap>();
		}
		private Dictionary<GID, AxisDistilleItemMap> _MapDict;
		private List<AxisDistilleItemMap> _MapList;
		#endregion
		#region SESTAVENÍ MAPY
		/// <summary>
		/// Z dodaných dat plánu vydestiluje mapu plánu.
		/// Mapa je vhodná jako podklad k přeplánování.
		/// </summary>
		/// <param name="axisHeap"></param>
		/// <returns></returns>
		internal static AxisDistilleMap DistillateAxisMapFrom(AxisHeapCls axisHeap)
		{
			AxisDistilleMap map = new AxisDistilleMap();

			map._FillFrom(axisHeap);
			map._CalcBeginSequence();
			map._SortByBeginSequence();
			map._InitPointers();

			return map;
		}
		/// <summary>
		/// Naplní do sebe do _MapDict všechny položky z axisHeap.AxisS a axisHeap.TaskC.
		/// Neřeší CalcSequence ani Sort.
		/// </summary>
		/// <param name="axisHeap"></param>
		private void _FillFrom(AxisHeapCls axisHeap)
		{
			this._MapDict = new Dictionary<GID, AxisDistilleItemMap>();

			foreach (MaterialPlanAxisItemCls axisItem in axisHeap.AxisS.Values)
				this._MapDict.Add(axisItem.AxisGID, new AxisDistilleItemMap(axisItem));

			foreach (CapacityPlanWorkItemCls taskItem in axisHeap.TaskC.Values)
				this._MapDict.Add(taskItem.TaskGID, new AxisDistilleItemMap(taskItem));

			foreach (PlanningItemRelationCls relItem in axisHeap.Relations.Values)
				this._AddRelation(relItem);
		}
		/// <summary>
		/// Přidá jeden vztah
		/// </summary>
		/// <param name="relItem"></param>
		private void _AddRelation(PlanningItemRelationCls relItem)
		{
			switch (relItem.Relation)
			{
				case PlanningItemRelationType.AxisBalance:
				case PlanningItemRelationType.AxisTransfer:
				case PlanningItemRelationType.AxisToTask:
				case PlanningItemRelationType.TaskToAxisFinal:
				case PlanningItemRelationType.TaskToAxisByProductSuitable:
				case PlanningItemRelationType.TaskToAxisByProductDissonant:
				case PlanningItemRelationType.TaskInner:
					this._AddRelation(relItem.ItemGIDPrev, relItem.ItemGIDNext);
					break;
			}
		}
		/// <summary>
		/// Přidá jeden vztah
		/// </summary>
		/// <param name="prevGId"></param>
		/// <param name="nextGId"></param>
		private void _AddRelation(GID prevGId, GID nextGId)
		{
			// Jednostranné vztahy pro nás nemají význam:
			if (prevGId.IsEmpty || nextGId.IsEmpty)
				return;

			// Položky Item na straně Prev, Next:
			AxisDistilleItemMap prevItem, nextItem;
			if (this._MapDict.TryGetValue(prevGId, out prevItem) && this._MapDict.TryGetValue(nextGId, out nextItem))
			{
				prevItem.AddNodeNext(nextItem);
				nextItem.AddNodePrev(prevItem);
			}
		}
		/// <summary>
		/// Do jednotlivých svých Items vypočte jejich BeginSequence
		/// </summary>
		private void _CalcBeginSequence()
		{
			IEnumerable<AxisDistilleItemMap> firstList = this._MapDict.Values.Where(item => item.NodesPrev.Count == 0);
			foreach (AxisDistilleItemMap firstItem in firstList)
				this._CalcBeginSequenceOne(firstItem);
		}
		/// <summary>
		/// Vypočte BeginSequence do daného Item a do všech NextItem, pokud dosud nebyly určeny
		/// </summary>
		/// <param name="item"></param>
		private void _CalcBeginSequenceOne(AxisDistilleItemMap item)
		{
			// Kontrola všech Next prvků:
			List<AxisDistilleItemMap> nextList = new List<AxisDistilleItemMap>();
			foreach (AxisDistilleItemMap next in item.NodesNext)
			{
				bool eqTime = (item.TimeOriginal.Begin == next.TimeOriginal.Begin);
				bool runNext = false;
				if (eqTime)
				{
					next.BeginSequence = item.BeginSequence + 1;
					runNext = true;
				}
				else
				{
					runNext = (!next.NextSequenceIsCalculated);
				}
				if (runNext)
					nextList.Add(next);
			}
			item.NextSequenceIsCalculated = true;

			// Rekurze do vyšší vrstvy pro ty prvky Next, kde je toho zapotřebí:
			foreach (AxisDistilleItemMap next in nextList)
				_CalcBeginSequenceOne(next);
		}
		/// <summary>
		/// Setřídí data v mapě podle data počátku a podle BeginSequence
		/// </summary>
		private void _SortByBeginSequence()
		{
			this._MapList = new List<AxisDistilleItemMap>(this._MapDict.Values);
			this._MapList.Sort(AxisDistilleItemMap.CompareByBeginSequence);
		}
		/// <summary>
		/// 
		/// </summary>
		private void _InitPointers()
		{ }
		#endregion
		#region PROPERTY
		/// <summary>
		/// Dictionary s jednotlivými prvky mapy. Jde o referenci na živý objekt.
		/// </summary>
		internal Dictionary<GID, AxisDistilleItemMap> MapDict { get { return this._MapDict; } }
		/// <summary>
		/// List s jednotlivými prvky mapy. Jde o referenci na živý objekt. 
		/// List je setříděn datem počátku originálního času (TimeOriginal.Begin) a v rámci data pak pořadím (BeginSequence).
		/// Jde o stabilní objekt (není to objekt ad-hoc vytvořený a vrácený v get accesoru).
		/// </summary>
		internal List<AxisDistilleItemMap> MapList { get { return this._MapList; } }
		#endregion
		#region POSKYTOVÁNÍ INFORMACÍ, SEZNAMŮ POLOŽEK
		/// <summary>
		/// Najde a vrátí item z mapy podle jeho GIDu.
		/// Pokud se nenajde, vrací null.
		/// </summary>
		/// <param name="itemGId"></param>
		/// <returns></returns>
		internal AxisDistilleItemMap GetItem(GID itemGId)
		{
			AxisDistilleItemMap item;
			if (this._MapDict.TryGetValue(itemGId, out item))
				return item;
			return null;
		}
		/// <summary>
		/// Vrátí seznam všech fixovaných položek
		/// </summary>
		/// <returns></returns>
		internal List<AxisDistilleItemMap> GetFixedItemList()
		{
			return this._MapList.Where(item => item.IsFixed).ToList();
		}
		/// <summary>
		/// Vrátí seznam všech nefixovaných položek
		/// </summary>
		/// <returns></returns>
		internal List<AxisDistilleItemMap> GetNonFixedItemList()
		{
            List<AxisDistilleItemMap> nonFixedList = this._MapList.Where(item => !item.IsFixed).ToList();
            ItemMapComparer comparer = new ItemMapComparer();
            List<AxisDistilleItemMap> uniqueList = nonFixedList.Distinct(comparer).ToList();
            return uniqueList;

			// return this._MapList.Where(item => !item.IsFixed).ToList();
		}
        /// <summary>
        /// Komparátor použitelný pro sestavení Distinct seznamu prvků třídy AxisDistilleItemMap, kde Distinct se vyhodnocuje podle 
        /// </summary>
        protected class ItemMapComparer : IEqualityComparer<AxisDistilleItemMap>
        {
            int IEqualityComparer<AxisDistilleItemMap>.GetHashCode(AxisDistilleItemMap a)
            {
                if (a == null) return 0;
                return a.ItemType.GetHashCode() ^ a.ItemGID.GetHashCode();
            }
            bool IEqualityComparer<AxisDistilleItemMap>.Equals(AxisDistilleItemMap a, AxisDistilleItemMap b)
            {
                if (a == null && b == null) return true;
                if (a == null || b == null) return false;
                return (a.ItemType == b.ItemType && a.ItemGID == b.ItemGID);
            }
		}
		/// <summary>
		/// Vrátí seznam položek, které jsou před daným prvkem, a které nejsou fixované.
		/// Vstupní prvek může být fixovaný.
		/// Vstupní prvek se nedostává do výstupního seznamu.
		/// Vrácený seznam je setříděn podle data počátku a BeginSequence (komparátor AxisDistilleItemMap.CompareByBeginSequence).
		/// </summary>
		/// <param name="rootItemGId">GID výchozího prvku</param>
		/// <returns></returns>
		internal List<AxisDistilleItemMap> GetPrevUnFixedItemList(GID rootItemGId)
		{
			AxisDistilleItemMap rootItem = GetItem(rootItemGId);
			return GetPrevUnFixedItemList(rootItem);
		}
		/// <summary>
		/// Vrátí seznam položek, které jsou před daným prvkem, a které nejsou fixované.
		/// Vstupní prvek může být fixovaný.
		/// Vstupní prvek se nedostává do výstupního seznamu.
		/// Vrácený seznam je setříděn podle data počátku a BeginSequence (komparátor AxisDistilleItemMap.CompareByBeginSequence).
		/// </summary>
		/// <param name="rootItem">Výchozí prvek</param>
		/// <returns></returns>
		internal List<AxisDistilleItemMap> GetPrevUnFixedItemList(AxisDistilleItemMap rootItem)
		{
			if (rootItem == null) return new List<AxisDistilleItemMap>();
			Dictionary<GID, AxisDistilleItemMap> map = new Dictionary<GID,AxisDistilleItemMap>();
			this._GetPrevUnFixedItemListNode(rootItem, ref map);
			List<AxisDistilleItemMap> result = new List<AxisDistilleItemMap>(map.Values);
			result.Sort(AxisDistilleItemMap.CompareByBeginSequence);
			return result;
		}
		/// <summary>
		/// Rekurzivně projde daný node a jeho Prev nody, a jeho Prev nody zapíše do předané mapy.
		/// Tedy: zadaný rootItem do mapy nepředává, a ani netestuje jeho stav IsFixed.
		/// Pokud daný rootItem nemá žádné NodesPrev, pak nic neprovede.
		/// Pokud je někde mezi rootItem.NodesPrev nějaký item, který je IsFixed, tak ten do mapy nepřidává a ani neprochází jeho NodesPrev.
		/// Pokud by někde došlo k zacyklení, pak nezabloudí (odmítne se vydat cestou, kterou už jednou šel).
		/// </summary>
		/// <param name="rootItem">Vstupní prvek</param>
		/// <param name="map">Vytvářená mapa Prev</param>
		private void _GetPrevUnFixedItemListNode(AxisDistilleItemMap rootItem, ref Dictionary<GID, AxisDistilleItemMap> map)
		{
			foreach (AxisDistilleItemMap prev in rootItem.NodesPrev)
			{
				if (prev.IsFixed) continue;
				if (!map.ContainsKey(prev.ItemGID))
				{
					map.Add(prev.ItemGID, prev);
					this._GetPrevUnFixedItemListNode(prev, ref map);
				}
			}
		}
		#endregion
		#region SEKVENČNÍ PŘÍSTUP K PRVKŮM PRO JEJICH TERMÍNOVÁNÍ
		/// <summary>
		/// Metoda připraví interní seznamy pro vyhledávání prvků v daném směru
		/// </summary>
		/// <param name="timeDir"></param>
		internal void PrepareForSequentialRegister(TimeRange.TimeDirection timeDir)
		{
			this.SequentTimeDir = timeDir;
			this.SequentInitialized = false;
			this.SequentForce = false;
			this.SequentCompleted = false;
			this.SequentNextFromIndex = 0;
			this.SequentLastGetIndex = -1;
			this.SequentFirstUnfinishedIndex = 0;
			this.SequentSortedList = new List<AxisDistilleItemMap>(this._MapList);
			switch (timeDir)
			{
				case TimeRange.TimeDirection.ToFuture:
					this.SequentSortedList.Sort(AxisDistilleItemMap.CompareByBeginSequence);
					break;
				case TimeRange.TimeDirection.ToHistory:
					this.SequentSortedList.Sort(AxisDistilleItemMap.CompareByEndSequence);
					break;
				default:
					Throw.SysError(MessageInfo.Get("Příprava mapy položek plánu: nelze připravit pro směr času %0, jen ToFuture nebo ToHistory.", timeDir));
					break;
			}
			this.SequentInitialized = true;
		}
		/// <summary>
		/// Najde a vrátí další položku k sekvenční registraci.
		/// Směr sekvenční registrace je určen v metodě this.PrepareForSequentialRegister(), jejím parametrem timeDir.
		/// Technika: vrací se vždy první položka (v daném směru), která má vyřešeny všechny svoje předchůdce (v daném směru), anebo žádné nemá.
		/// Pořadí je dané směrem času.
		/// Položky, které by neměly vyřešené všechny své předchůdce, přeskakuji.
		/// Pokud nenajdu žádnou položku, vrátím null.
		/// Pokud dojdu až na konec seznamu, a přitom nebudu mít vyřešené všechny položky, přepnu se do režimu Force, 
		/// a budu vracet nevyřešené položky i bez ohledu na vyřešenost předchůdců.
		/// </summary>
		/// <returns></returns>
		internal AxisDistilleItemMap GetNextItemForRegister()
		{
			if (!this.SequentInitialized)
				Throw.SysError(MessageInfo.Get("Nelze použít sekvenční přístup k prvkům mapy položek plánu, sekvenční přístup není inicializován."));
			if (this.SequentCompleted)
				Throw.SysError(MessageInfo.Get("Sekvenční přístup byl ukončen."));

			int itemCnt = this.SequentSortedList.Count;
			if (itemCnt == 0) return null;

			TimeRange.TimeDirection timeDir = this.SequentTimeDir;
			int fromMode = (!this.SequentForce ? 0 : 1);          // Další kód je prováděn ve dvou režimech: 0=beru jen vhodné prvky (finalizované předchůdce), 1=beru všechny nefinalizované prvky
			for (int mode = fromMode; mode < 2; mode++)
			{	// Dva cykly: nejdřív mode = 0 (beru jen vhodné prvky (nefinalizovaný prvek, finalizované předchůdce),
				// a potom mode = 1 (beru všechny dosud nefinalizované prvky):
				bool forceMode = (mode == 1);                     // Režim Force v tomto kole
				int fromIndex = this.SequentNextFromIndex;        // Začnu hledat další prvek k registraci od indexu, odkud se má začít (viz dále)
				bool allPreviousFinished = true;                  // Příznak, že všechny dosavadní (předchozí) prvky jsou hotové (index prvního prvku za posledním prvkem kontinuální řady se ukládá do this.SequentFirstUnfinishedIndex)
				allPreviousFinished = (this.SequentNextFromIndex <= this.SequentFirstUnfinishedIndex);
				for (int i = fromIndex; i < itemCnt; i++)         // Hodnota fromIndex může být rovna itemCnt (protože v předešlém cyklu se do this.SequentNextFromIndex nastavila hodnota i+1), pak cyklus nezačne a jdeme na kód za jeho koncem.
				{
					AxisDistilleItemMap mapItem = this.SequentSortedList[i];
					if (mapItem.IsFinished) continue;             // Hotové prvky neřeším.
					if (forceMode)
					{	// Jsem v nuceném režimu = nezajímá mě, zda má prvek finalizované všechny předchůdce (nevolám metodu  mapItem.AllOtherNodeFinished(...))
						this.SequentLastGetIndex = i;             // Tento prvek byl vydán při posledním běhu metody GetNextItemForRegister()
						this.SequentNextFromIndex = i + 1;        // Od tohoto prvku budu příště hledat, pokud nedojde k finalizaci vráceného prvku
						return mapItem;
					}

					// Tento prvek není finalizován, a jsem v režimu NotForce (forceMode == false).
					// Průběžně si aktualizuji index this.SequentFirstUnfinishedIndex = index prvního prvku, který není finalizován (další viz metoda this.SequentSetFinalTimeRange()):
					if (allPreviousFinished)
					{
						this.SequentFirstUnfinishedIndex = i;
						allPreviousFinished = false;              // Odteď už se nebude ukládat index do this.SequentFirstUnfinishedIndex.
					}

					// Zajímá mě, zda má prvek finalizovány své předchůdce:
					bool allOtherNodeFinished = mapItem.AllOtherNodeFinished(timeDir, true);
					if (allOtherNodeFinished)
					{	// Tento prvek není finalizován, a všechny jeho předchůdce jsou finalizovány => vydám tento prvek:
						this.SequentLastGetIndex = i;             // Tento prvek byl vydán při posledním běhu metody GetNextItemForRegister()
						this.SequentNextFromIndex = i + 1;        // Od tohoto prvku budu příště hledat, pokud nedojde k finalizaci vráceného prvku
						return mapItem;
					}
				}
				if (forceMode)
				{	// Právě doběhl cyklus v režimu 1 = force: skončím a vypadnu. Další akce nejsou pro mě.
					this.SequentCompleted = true;
					break;
				}

				// Pokud přecházím z režimu 0 do režimu 1 (forceMode bude == true), pak si něco musím přenastavit:
				this.SequentForce = true;                         // Bude režim Force
				this.SequentNextFromIndex = 0;                    // Režim Force bude procházet všechny prvky, od indexu 0
			}
			return null;
		}
		/// <summary>
		/// Tuto metodu je třeba zavolat po otermínování prvku v procesu přeplánování.
		/// Metoda vloží daný čas do mapItem.TimeFinished, nastaví stav položky mapItem.ItemState = Finished.
		/// Zavoláním této metody bude optimalizován chod metody GetNextItemForRegister().
		/// </summary>
		/// <param name="mapItem">Prvek, který je právě finalizován (otermínován)</param>
		/// <param name="timeFinished">Finální čas</param>
		internal void SequentSetFinalTimeRange(AxisDistilleItemMap mapItem, TimeRange timeFinished)
		{
			// 1. Vložením času bude prvek finalizován:
			mapItem.TimeFinished = timeFinished;
			mapItem.ItemState = AxisDistilleItemPlanState.Finished;

			// 2. Optimalizace ukazatelů pro metodu GetNextItemForRegister():
			//    Pokud nejsem v režimu Force, pak zajistím, že v příštím volání metody GetNextItemForRegister() 
			//      projdu znovu všechny "nižší" nefinalizované prvky, počínaje od prvního prvku, který dosud není finalizován.
			//    Důvod: finalizací zdejšího prvku (pod bodem 1 v této metodě) mohlo dojít k tomu, 
			//      že některý kdysi předešlý prvek (který před tím neměl finalizované své předchůdce),
			//      je nyní již má finalizované. 
			//    Například: když prvek 25 má za předchůdce prvek 32, 
			//      v prvním chodu tedy prvek 25 nemá finalizované své předchůdce,
			//      ale po dokončení (otermínování) prvku 32 zajistím návrat k prvku 25 (=> viz následující řádek!), 
			//      při tom zjistím, že on (prvek 25) má už všechny své předchůdce finalizované, 
			//      a tak mohu finalizovat (otermínovat) i prvek 25:
			if (!this.SequentForce && mapItem.IsFinished)
			{
				// Pokud jsme v posledním chodu vrátili index (this.SequentLastGetIndex) shodný 
				//    s indexem prvního nedokončeného prvku (this.SequentFirstUnfinishedIndex),
				//    a protože nyní jsme v metodě pro finalizaci posledně vráceného prvku, 
				// můžeme tvrdit, že první nedokončený prvek (this.SequentFirstUnfinishedIndex) je už ten prvek následující (++):
				if (this.SequentFirstUnfinishedIndex == this.SequentLastGetIndex)
					this.SequentFirstUnfinishedIndex++;
				this.SequentNextFromIndex = this.SequentFirstUnfinishedIndex;
			}
		}
		/// <summary>
		/// Směr času pro sekvenční přístup
		/// </summary>
		public TimeRange.TimeDirection SequentTimeDir { get; protected set; }
		/// <summary>
		/// Příznak, že sekvenční přístup je korektně inicializován.
		/// </summary>
		public bool SequentInitialized { get; protected set; }
		/// <summary>
		/// Index prvku, od kterého příště začneme hledat další dosud nefinalizovaný záznam
		/// </summary>
		public int SequentNextFromIndex { get; protected set; }
		/// <summary>
		/// Index prvku, který obsahuje první dosud nefinalizovaný záznam
		/// </summary>
		public int SequentFirstUnfinishedIndex { get; protected set; }
		/// <summary>
		/// Index prvku, který posledně vrátila metoda GetNextItemForRegister().
		/// </summary>
		public int SequentLastGetIndex { get; protected set; }
		/// <summary>
		/// Příznak, že sekvenční přístup detekoval problém, kdy existují už jen nevyřešené položky, jejichž předchůdci jsou rovněž nevyřešení.
		/// </summary>
		public bool SequentForce { get; protected set; }
		/// <summary>
		/// Příznak, že sekvenční přístup je dokončen. Obsahuje true po vydání posledního prvku.
		/// </summary>
		public bool SequentCompleted { get; protected set; }
		/// <summary>
		/// Seznam prvků setříděný podle počátku / konce, vždy ASC (ASC = pro snadnější logické pochopení)
		/// </summary>
		public List<AxisDistilleItemMap> SequentSortedList { get; protected set; }
		#endregion
	}
	#endregion
	#region CLASS AxisDistilleItemMap : jedna položka mapy plánu, mapa je destilována z plánu a obsahuje zjednodušenou jen kostru plánu
	/// <summary>
	/// AxisDistilleItemMap : jedna položka mapy plánu, mapa je destilována z plánu a obsahuje zjednodušenou jen kostru plánu
	/// </summary>
	internal class AxisDistilleItemMap
	{
		#region KONSTRUKCE
		/// <summary>
		/// Diskrétní konstruktor
		/// </summary>
		/// <param name="itemGId">GID položky</param>
		/// <param name="itemType">Typ položky</param>
		/// <param name="itemQty">Množství na položce</param>
		/// <param name="timeOriginal">Původní časový rozsah</param>
		/// <param name="isFixed">Příznak fixace položky</param>
		internal AxisDistilleItemMap(GID itemGId, AxisDistilleItemType itemType, decimal itemQty, TimeRange timeOriginal, bool isFixed)
		{
			this._Init(itemGId, itemType, itemQty, timeOriginal, IsFixed);
		}
		/// <summary>
		/// Konstruktor na základě dat osy S
		/// </summary>
		/// <param name="axisItem"></param>
		internal AxisDistilleItemMap(MaterialPlanAxisItemCls axisItem)
		{
			this._Init(axisItem.AxisGID, AxisDistilleItemType.AxisS, axisItem.Qty, axisItem.DatePlanTime, axisItem.IsFixedAxis);
		}
		/// <summary>
		/// Konstruktor na základě dat kapacitního úkolu
		/// </summary>
		/// <param name="taskItem"></param>
		internal AxisDistilleItemMap(CapacityPlanWorkItemCls taskItem)
		{
			this._Init(taskItem.TaskGID, AxisDistilleItemType.TaskC, taskItem.QtyRequired, taskItem.TimeWork, taskItem.IsFixedTask);
		}
		/// <summary>
		/// Iniciace všech hodnot prvku
		/// </summary>
		/// <param name="itemGId">GID položky</param>
		/// <param name="itemType">Typ položky</param>
		/// <param name="itemQty">Množství na položce</param>
		/// <param name="timeOriginal">Původní časový rozsah</param>
		/// <param name="isFixed">Příznak fixace položky</param>
		private void _Init(GID itemGId, AxisDistilleItemType itemType, decimal itemQty, TimeRange timeOriginal, bool isFixed)
		{
			this.ItemGID = itemGId;
			this.ItemType = itemType;
			this.ItemState = AxisDistilleItemPlanState.Original;
			this.ItemQty = itemQty;
			this.TimeOriginal = timeOriginal;
			this.IsFixed = isFixed;
			this.TimeFinished = TimeRange.Empty;
			this.NodesPrev = new List<AxisDistilleItemMap>();
			this.NodesNext = new List<AxisDistilleItemMap>();
			this.BeginSequence = 0;
			this.NextSequenceIsCalculated = false;
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			string text = "{" + this.ItemType.ToString() + ": " + this.ItemGID + "; Qty=" + this.ItemQty + "; ";
			if (this.IsFinished)
				text += ";TimeFinished=" + this.TimeFinished.ToString();
			else
				text += ";TimeOriginal=" + this.TimeOriginal.ToString();
			text += "; Prev=" + this.NodesPrev.Count + "; Next=" + this.NodesNext.Count + "}";
			return text;
		}
		#endregion
		#region PROPERTIES
		/// <summary>
		/// GID položky
		/// </summary>
		public GID ItemGID { get; protected set; }
		/// <summary>
		/// Typ položky (AxisS nebo TaskC)
		/// </summary>
		public AxisDistilleItemType ItemType { get; protected set; }
		/// <summary>
		/// Stav položky vzhledem k plánovacímu procesu
		/// </summary>
		public AxisDistilleItemPlanState ItemState { get; internal set; }
		/// <summary>
		/// Množství, které se na položce pohybuje (plus = příjem do skladu nebo množství výroby, mínus = výdej ze skladu)
		/// </summary>
		public decimal ItemQty { get; protected set; }
		/// <summary>
		/// Čas položky originální
		/// </summary>
		public TimeRange TimeOriginal { get; protected set; }
		/// <summary>
		/// Příznak, že položka je fixovaná k času
		/// </summary>
		public bool IsFixed { get; protected set; }
		/// <summary>
		/// Čas položky po přeplánování
		/// </summary>
		public TimeRange TimeFinished { get; internal set; }
		/// <summary>
		/// Předchozí položky
		/// </summary>
		public List<AxisDistilleItemMap> NodesPrev { get; protected set; }
		/// <summary>
		/// Následující položky
		/// </summary>
		public List<AxisDistilleItemMap> NodesNext { get; protected set; }
		/// <summary>
		/// Pořadové číslo této položky v bloku následných položek, pokud mají shodný čas Begin.
		/// Pokud mám za sebou např. tři Items, které mají nulovou délku a mají tedy shodný čas, pak první má BeginSequence = 0, 
		/// další má BeginSequence = 1 a poslední BeginSequence = 2.
		/// Tato hodnota se použije při setřídění celkové mapy podle data Begin a BeginSequence, viz zdejší statická metoda CompareByBeginSequence().
		/// </summary>
		internal int BeginSequence { get; set; }
		/// <summary>
		/// Příznak, že tato položka a všechny její Next položky již mají vypočtenu hodnotu BeginSequence.
		/// </summary>
		internal bool NextSequenceIsCalculated { get; set; }
		/// <summary>
		/// true, pokud je item již otermínován (jeho čas this.TimeFinished není empty).
		/// </summary>
		internal bool IsFinished { get { return !this.TimeFinished.IsEmpty; } }
		#endregion
		#region ADD NODES
		internal void AddNodePrev(AxisDistilleItemMap prevItem)
		{
			this._AddNodeToList(prevItem, this.NodesPrev);
		}

		internal void AddNodeNext(AxisDistilleItemMap nextItem)
		{
			this._AddNodeToList(nextItem, this.NodesNext);
		}

		private void _AddNodeToList(AxisDistilleItemMap item, List<AxisDistilleItemMap> list)
		{
			if (item != null)
			{
				if (!list.Exists(delegate(AxisDistilleItemMap i) { return i.ItemGID == item.ItemGID; }))
					list.Add(item);
			}
		}
		#endregion
		#region STATIC KOMPARÁTORY
		/// <summary>
		/// Komparátor určený pro třídění: TimeOriginal.Begin ASC, BeginSequence ASC.
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		internal static int CompareByBeginSequence(AxisDistilleItemMap a, AxisDistilleItemMap b)
		{
			int cmp = a.TimeOriginal.Begin.CompareTo(b.TimeOriginal.Begin);
			if (cmp != 0) return cmp;
			cmp = a.BeginSequence.CompareTo(b.BeginSequence);
			return cmp;
		}
		/// <summary>
		/// Komparátor určený pro třídění: TimeOriginal.End DESC, BeginSequence DESC.
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		internal static int CompareByEndSequence(AxisDistilleItemMap a, AxisDistilleItemMap b)
		{
			int cmp = b.TimeOriginal.End.CompareTo(a.TimeOriginal.End);
			if (cmp != 0) return cmp;
			cmp = b.BeginSequence.CompareTo(a.BeginSequence);
			return cmp;
		}
		#endregion
		/// <summary>
		/// Metoda prověří, zda jsou všechny prvky na dané straně hotové (IsFinished).
		/// (ToFuture: Next, ToHistory: Prev; reverse = opačně).
		/// Vrací true, pokud jsou všechny prvky na dané straně 
		/// </summary>
		/// <param name="timeDir">Směr času, kontroluje prvky na straně: ToFuture: NodesNext, ToHistory: NodesPrev (při reverse = false)</param>
		/// <param name="reverse">Obraceč směru času. Při hodnotě true kontroluje prvky na straně ToFuture: NodesPrev, ToHistory: NodesNext.</param>
		/// <returns></returns>
		internal bool AllOtherNodeFinished(TimeRange.TimeDirection timeDir, bool reverse)
		{
			// Seznam sousedních prvků
			List<AxisDistilleItemMap> list = this.GetOtherItems(timeDir, reverse);

			// Pokud je některý z prvků !IsFinished, vracím false:
			// Pokud jsou všechny prvky IsFinished, anebo není žádný prvek, vracím true:
			foreach (AxisDistilleItemMap mapItem in list)
			{
				if (!mapItem.IsFinished) return false;
			}
			return true;
		}
		/// <summary>
		/// Najde a vrátí čas zahájení pro tuto položku, vycházejíc z časů dokončení předchozích úkolů (v daném směru).
		/// Pokud položka předchozího úkolu není IsFinished, ignoruje se.
		/// Pokud se nenajde žádná položka, vrací se DateTime.MinValue.
		/// </summary>
		/// <param name="timeDir">Směr plánování, určuje orientaci = které prvky jsou předešlé, a který jejich čas přebíráme: 
		/// ToFuture: předešlé jsou NodesPrev, přebírám Max(Prev.End),
		/// ToHistory: předešlé jsou NodesNext, přebírám Min(Next.Begin).
		/// </param>
		/// <param name="fromAllItems">Požadavek: 
		/// true = Výsledek musí být určen ze všech předchozích položek. Pokud by jedna jediná nebyla IsFinished, musí se vrátit MinValue.
		/// false = Výsledek se má určit jen z IsFinished položek, nedokončené položky se budou ignorovat (jsme v nouzovém = Force režimu).</param>
		/// <param name="hasPrevItems">Out informace, zda položka má nějaké předchozí prvky. Pokud nemá, je zde false.</param>
		/// <returns></returns>
		internal DateTime GetStartTimeFromPrevItems(TimeRange.TimeDirection timeDir, bool fromAllItems, out bool hasPrevItems)
		{
			// Seznam sousedních prvků
			List<AxisDistilleItemMap> list = this.GetOtherItems(timeDir, true);         // Pro ToFuture = Prev, pro ToHistory = Next
			hasPrevItems = (list.Count > 0);

			// Výsledek:
			DateTime startTime = DateTime.MinValue;
			foreach (AxisDistilleItemMap item in list)
			{
				if (item.IsFinished)
				{
					DateTime itemTime;
					switch (timeDir)
					{
						case TimeRange.TimeDirection.ToFuture:
							itemTime = item.TimeFinished.End;
							if (itemTime > DateTime.MinValue && (startTime == DateTime.MinValue || itemTime > startTime))
								startTime = itemTime;
							break;
						case TimeRange.TimeDirection.ToHistory:
							itemTime = item.TimeFinished.Begin;
							if (itemTime > DateTime.MinValue && (startTime == DateTime.MinValue || itemTime < startTime))
								startTime = itemTime;
							break;
					}
				}
				else if (fromAllItems)
					// Tato položka (item) není IsFinished.
					// Pokud je požadavek fromAllItems == true, pak mi tento stav vadí => taková položka mi zkazí celkový dojem, skončím bez výsledku:
					return DateTime.MinValue;
				// Pokud není položka IsFinished, a přitom není požadavek fromAllItems == true, pak takovou položku ignoruji a hledám dál...
			}
			return startTime;
		}
		/// <summary>
		/// Metoda vrátí seznam prvků Prev nebo Next, podle požadovaného směru.
		/// (ToFuture: Next, ToHistory: Prev; reverse = opačně).
		/// Vrací true, pokud jsou všechny prvky na dané straně 
		/// </summary>
		/// <param name="timeDir">Směr času, kontroluje prvky na straně: ToFuture: NodesNext, ToHistory: NodesPrev (při reverse = false)</param>
		/// <param name="reverse">Obraceč směru času. Při hodnotě true kontroluje prvky na straně ToFuture: NodesPrev, ToHistory: NodesNext.</param>
		/// <returns></returns>
		internal List<AxisDistilleItemMap> GetOtherItems(TimeRange.TimeDirection timeDir, bool reverse)
		{
			List<AxisDistilleItemMap> list = null;
			switch (timeDir)
			{
				case TimeRange.TimeDirection.ToFuture:
					list = (reverse ? this.NodesPrev : this.NodesNext);
					break;
				case TimeRange.TimeDirection.ToHistory:
					list = (reverse ? this.NodesNext : this.NodesPrev);
					break;
			}
			if (list == null)
				Throw.SysError(MessageInfo.Get("Nelze určit směr času, nebo seznam prvků na dané straně (timeDir = %0, reverse = %1, list = null).", timeDir, reverse));
			return list;
		}
	}
	/// <summary>
	/// Typy položek Item v mapě
	/// </summary>
	internal enum AxisDistilleItemType
	{
		None = 1,
		AxisS,
		TaskC
	}
	/// <summary>
	/// Stav položek v mapě
	/// </summary>
	internal enum AxisDistilleItemPlanState
	{
		None = 1,
		/// <summary>
		/// Položka je v původním stavu, jak byla nalezena v původním plánu.
		/// </summary>
		Original,
		/// <summary>
		/// Položka je odpojená od plánu, má odregistrované kapacity.
		/// </summary>
		Detached,
		/// <summary>
		/// Položka je už nově otermínovaná.
		/// </summary>
		Finished
	}
	#endregion
}
